package com.shaoyu.tools.event.core;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.eventbus.SubscriberExceptionContext;
import com.google.common.eventbus.SubscriberExceptionHandler;
import com.google.common.reflect.TypeToken;
import org.apache.commons.collections4.CollectionUtils;

import java.util.List;
import java.util.Set;


/**
 * @author shaoyu
 * @date 2022/11/2 17:27
 */
public class SubscriberExceptionHandlerDispatcher implements SubscriberExceptionHandler {

    private final List<CustomSubscribeExceptionHandler> handlers ;
    private final Multimap<Class,CustomSubscribeExceptionHandler> handlerMaps ;

    public SubscriberExceptionHandlerDispatcher(List<CustomSubscribeExceptionHandler> customSubscribeExceptionHandlers){
        handlers = customSubscribeExceptionHandlers;
        handlers.add(CustomSubscribeExceptionHandler.LOGGING_HANDLER);
        handlerMaps = HashMultimap.create();
    }
    @Override
    public void handleException(Throwable throwable, SubscriberExceptionContext subscriberExceptionContext) {
        Class eventClass = subscriberExceptionContext.getEvent().getClass();
        if(CollectionUtils.isEmpty(handlerMaps.get(eventClass))){
            generateEventClassExceptionHandlers(eventClass);
        }
        handlerMaps.get(eventClass).forEach(handler ->
                handler.handleException(throwable, subscriberExceptionContext));
    }

    private void generateEventClassExceptionHandlers(Class eventClass) {
        synchronized (handlerMaps){
            if(CollectionUtils.isEmpty(handlerMaps.get(eventClass))){
                synchronized (handlerMaps){
                    handlers.forEach(handler -> {
                        Class eventClassKey = handler.getEventClass();
                        if(eventImplementsClass(eventClass,eventClassKey)){
                            handlerMaps.put(eventClass,handler);
                        }
                    });
                }
            }
        }
    }

    private boolean eventImplementsClass(Class<?> eventClass, Class clz) {
        Set<? extends Class<?>> supertypes = TypeToken.of(eventClass).getTypes().rawTypes();
        return supertypes.contains(clz);
    }
}
